/////////////////////////////////////////////////////////////////////////////
// 这个文件是 JSI 项目的一部分
//
// Copyright (c) 2006 - 2007 (www.xidea.org)
//
// 要查看完整的版权信息和许可信息，请查看源代码中附带的 LICENSE.txt 文件，
// 或者访问 http://xidea.org/ 获得详细信息。
/////////////////////////////////////////////////////////////////////////////

/**
 * 定义 $JSI, 并初始化 $JSI 运行环境
 *
 * 对于大部分 $JSI 的组件，都要求预先初始化 $JSI 环境。
 * 在页面中需要通过 <script> 标签 载入该文件，
 * 即可完成 JSI 运行环境的初始化工作。
 * dependences: log.js, core.js, final-browser.js
 *
 * @package core
 * @copyright (c) 2006 - 2007 (www.xidea.org)
 * @author: dawei jin(jindw@xidea.org)
 * @author: DSONet (dsonet@msn.com)
 * @version $Id: boot-browser.js 29 2008-02-16 17:57:30Z dsonet $
 */

/*if ($JSI == null || ( typeof $JSI == "object" && $JSI != "[object $JSI]"))
{*/
/**
 * JSI对象
 * 提供框架基础服务，脚本及对象缓存等功能
  * @instanceof Function
 * @public
 */
$JSI = new function()
{
	/**
	 * $JSI的字符串表示
	 * @member $JSI
	 * @static
	 * @private
	 * @return {string} [object $JSI]
	 */
	this.toString = function ()
	{
		return "[object $JSI]";
	}
	if(typeof $JSI == "object" && $JSI != null)
	{
		for(var oName in $JSI)
			this[oName] = $JSI[oName];
	}
	var sharedXmlHttp//共享的XMLHTTPRequest对象
		, cachedScripts = {}//缓存的脚本集合
		, attributeMap = {}//属性集合
		, componentMap = {}//装饰器组件集合
		, cfgFile = this.configFile//配置文件
		, bootBase = this.bootBase//JSI脚本地址
		, scriptBase = this.scriptBase//脚本库地址
		, preLoad//预加载脚本
		, ua = navigator.userAgent
		, w = window
		, cf;//char at first

	if (scriptBase && scriptBase.charAt(scriptBase.length - 1) != "/")
	{
		scriptBase += "/";
	}

	bootBase = document.getElementsByTagName("script");
	bootBase = bootBase[bootBase.length - 1];
	cfgFile = bootBase.getAttribute("configFile");
	preLoad = bootBase.getAttribute("preLoad");
	bootBase = (bootBase.getAttribute("src") + "?").replace(/[^\/\\]+[\?#].*$/, "") || "./";//去掉文件名及QueryString与Hash
	/*bootBase = computeURL(bootBase.getAttribute("src").replace(/[^\/\\]+[\?#].*$/, ""));//需要检测绝对地址?*/

	if(preLoad)
	{
		/**
		 * 预加载的类
		 * @static
		 * @friend
		 * @member $JSI
		 * @type string
		 */
		this.preLoad = preLoad;
	}

	if (ua.indexOf("MSIE") > -1 && ua.indexOf("Opera") < 0)
	{
		document.write("<!--[if lt IE 5.5]><scr" + "ipt type='text/javascript' src='" + bootBase + "__res/iecompat.js'></scr" + "ipt><![endif]-->");
		//try{document.execCommand("BackgroundImageCache", false, true);}catch(e){}//解决背景事件重复加载的问题
	}

	if(cfgFile && !((cf = cfgFile.charAt(0)) == "/" || cf == "."  || cfgFile.indexOf("://") > -1))
	{
		cfgFile = bootBase + cfgFile;
	}

	/**
	 * 脚本库路径
	 * 默认为当前脚本所在的位置
	 * @public
	 * @member $JSI 
	 * @static
	 */
	this.scriptBase = scriptBase || bootBase;

	/**
	 * 配置脚本路径，默认为 config.js
	 * 可以在通过script标签上设置configFile改变此属性
	 * 若configFile地址起始未出现.或/或://协议字符，则认为其与当前script同目录.
	 * @public 
	 * @member $JSI 
	 * @static
	 */
	this.configFile = cfgFile || "";
	/**
	 * 启动脚本路径
	 * 为当前脚本所在的位置
	 * @public
	 * @member $JSI 
	 * @static
	 */
	this.bootBase = bootBase;
	
	/**
	 * 指示是框架否初始化
	 * 默认为false,window.onload事件里置为true
	 * @static
	 * @public
	 * @member $JSI
	 * @type boolean
	 */
	this.loaded = false;

	/**
	 * 加载指定地址的文本或XML文件，可发送数据及设定异步模式和回调函数
	 * 当回调参数为true值时，仅设定传输为异步模式。
	 * 要传送的数据应按如下格式：{data:"send to s", header:{itemName:itemValue}}
	 * @public
	 * @member $JSI
	 * @id $JSI.loadURL
	 * @param {string} url 文件地址
	 * @param {Function}  [callback] 回调函数，参数为传输对象
	 * @param {Object}  [data] 要传送的数据
	 * @return {string|void} 同步模式下返回结果文本
	 */
	this.loadURL = function(url, callback, data)
	{
		var method = (data = data || null) instanceof Object ? "POST" : "GET"
			//, cb = typeof callback == "function"
			, async = typeof callback == "function" ? true : false //|| callback === true
			, req = !async ? sharedXmlHttp || new XMLHttpRequest : new XMLHttpRequest
			, stus
			, e;
		sharedXmlHttp = null;
	
		req.open(method, url, async);
		/*netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
		netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
		req.overrideMimeType("text/plain");*///for firefox test

		if(data && data.header)
		{
			for (var o in data.header)
			{
				req.setRequestHeader(o, data.header[o]);
			}
		}

		if(async/* && cb*/)
		{
			req.onreadystatechange = function ()
			{
				callback(req);
				if(req.readyState == 4)//very improtant in IE,otherwise cause mleak
				{
					req = null;
				}
			}

			/*if (req.readyState == null){
				req.readyState = 0;
				req.addEventListener("load", function () {
					req.readyState = 4;
					req.onreadystatechange();
					}
				, false);
			}*/
		}

		try
		{
			//for ie file 404 will throw exception
			req.send(data && data.data);
		}
		catch (e)
		{
			$log.error("$JSI.loadURL加载文件失败:" + url, e);
			throw e;
		}
		finally
		{
			if(!async)
			{
				if(!e)
				{
					stus = req.status;
					if (stus >= 200 && stus < 300 || stus == 304 || !stus)
					{
						return /\/xml/.test(req.getResponseHeader("Content-Type")) ? req.responseXML : req.responseText;
					}
					else
					{
						$log.error("$JSI.loadURL加载文件失败:" + url, "状态码:" + stus);
					}
				}
				req.abort();
				sharedXmlHttp = req;
				req = null;
			}
		}
	}

	/**
	 * 设置JSI系统属性
	 * @public
	 * @member $JSI 
	 * @param {string} key 属性名 
	 * @param {mixed} value 属性值 
	 * @static 
	 */
	this.setAttribute = function(key, value)
	{
		attributeMap[key] = value;
	}
	/**
	 * 获取$JSI中指定名称的属性值
	 * @public
	 * @member $JSI
	 * @param {string} key 属性名 
	 * @return {mixed} 属性值
	 * @static
	 */
	this.getAttribute = function(key)
	{
		return attributeMap[key];
	}
	/**
	 * 获取脚本缓存。 
	 * @private
	 * @static
	 * @member $JSI
	 * @param {string} pkgName 包名 
	 * @param {string} fileName 文件名 
	 */
	this.getCacheScript = function(pkgName, fileName)
	{
		var cache = cachedScripts[pkgName];
		return cache ? cache[fileName || "__package__.js"]: null;
	}
	/**
	 * 添加脚本缓存。 若没有value参数，则认为file是一个包含文件路径和缓存内容的对象。
	 * @private 
	 * @static
	 * @member $JSI
	 * @param {string} pkg 包名称
	 * @param {string|ojbect} file 文件相对路径
	 * @param {string|Function} [value] 缓存函数或文本 
	 */
	this.addCacheScript = function(pkg, file, value)
	{
		if (cachedScripts[pkg])
		{
			pkg = cachedScripts[pkg];
			if (value == null)
			{
				for (var n in file)
				{
					pkg[n] = file[n]; 
				}
			}
			else
			{
				pkg[file] = value; 
			}
		}
		else
		{
			value == null ? cachedScripts[pkg] = file : (cachedScripts[pkg] = {})[file] = value;
		}
	}
	/**
	 * 注册组件对象，组件（如装饰器）注册接口 
	 * @friend
	 * @member $JSI
	 * @param {Object} decorator 组件对象，如装饰器对象。 
	 * @param {String} [id] 可选，默认采用组件对象的id属性 
	 * @static 
	 */
	this.addComponent = function(decorator, id)
	{
		componentMap[id || decorator.id] = decorator;
	}
	/**
	 * 获取组件对象 
	 * @friend
	 * @member $JSI
	 * @id $JSI.getComponent
	 * @param {string} id 组件对象的id属性，或注册时指定的id（这时，通过组件id无效） 
	 * @return {object} 指定id的已注册组件对象，如某个装饰器对象
	 */
	this.getComponent = function(id)
	{
		return componentMap[id];
	}
	/**
	 * 添加装饰包。
	 * @friend 
	 * @member $JSI 
	 * @id $JSI.addDecoratorProvider
	 * @param {string[]} arguments 第一个参数是包名，其他参数为别名,别名*表示默认包
	 * @static 
	 */
	this.addDecoratorProvider = function()
	{
		var key = "decoratorProviders";
		attributeMap[key] ? attributeMap[key].push(arguments) : attributeMap[key] = [arguments];
	}
	/**
	 * 准备执行装饰器任务,一般在配置文件（config&#46js）中调用;
	 * 程序将首先判断是否申明了装饰用的名称空间
	 * xmlns:d = "http://www.xidea.org/taglib/decorator" 
	 * 未申明则终止操作。
	 * 若不想默认启动装饰引擎，也可以在页面上调用（删除配置文件的调用语句）; 
	 * <br/><b>装饰引擎只在申明了命名空间的页面才有效，否则不会执行任何操作</b> 
	 * @friend
	 * @member $JSI
	 * @id $JSI.decorate
	 * @param {string} [enginePath] 装饰引擎名，可选，默认采用：org.xidea.decorator.DecoratorEngine
	 * @static
	 * @return {void}
	 */
	this.decorate = function(enginePath)
	{
		this.decorate = $JSI.$void;

		var detect
			, ua = navigator.userAgent
			, dns = "http://www.xidea.org/taglib/decorator"
			, ns
			, dd = document.documentElement
			, defErr = "装饰器名称空间已被占用:"
			, i;
		if (ua.indexOf("MSIE") > -1)
		{
			//for ie and opera
			if (ns = document.namespaces)
			{
				//for ie 5.5+
				for (i = ns.length - 1; i >= 0; i--)
				{
					if (ns.item(i).name.toLowerCase() == "d")
					{
						if (ns.item(i).urn == dns)
						{
							detect = true;
						}
						else
						{
							$log.info(defErr + ns.item(i).urn);
						}
						break;
					}
				}
			}
			else
			{
				//for ie5 or opera
				//判断是否需要装饰该网页
				detect = dd.outerHTML.indexOf(dns) > -1;
			}
			if (detect && ua.indexOf("Opera") < 0)
			{
				document.write("<style type='text/css'>d\\:*{behavior:url(" + this.bootBase + "__res/decorator.htc)}</style>");
				detect = true;
			}
		}
		else if (ns = dd.getAttribute("xmlns:d"))
		{
			if (ns == dns)
			{
				detect = true;
			}
			else
			{
				$log.info(defErr + ns);
			}
		}
		else if (ns = dd.outerHTML)
		{
			detect = ns.indexOf(dns) > -1;
		}

		//初始化装饰任务 
		if (detect)
		{
			$import(enginePath || "org.xidea.decorator.DecoratorEngine", function(DecoratorEngine)
			{
				$import("js.dom.Event", false, null).on(document, "domready", function()
				{
					if (!DecoratorEngine.mainEngine)
					{
						DecoratorEngine.mainEngine = new DecoratorEngine($JSI.getAttribute("decoratorProviders") || []);
						DecoratorEngine.mainEngine.start();
					}
				}
				, true);
			}
			, null);
		}
		enginePath = detect = ns = dd = ua = dns = defErr = i = null;
	}

	function init ()
	{
		$JSI.loaded = true;//is window loaded
		init = null;
	}
	function destroy()
	{
		sharedXmlHttp = null;
	}
	if (w.attachEvent)
	{
		w.attachEvent("onload", init);
		w.attachEvent("onunload", destroy);
	}
	else if (w.addEventListener)
	{
		w.addEventListener("load", init, false);
		w.addEventListener("onunload", destroy, false);
	}
	else
	{
		w.onload = init;
		w.onunload = destroy;
	}
	cfgFile = scriptBase = bootBase = preLoad = ua = cf = oName = w = /*dirTrimer = homeFormater = base = baseTags = computeURL = */null;
};

/** 
 * global context eval
 * @typeof function
 * @id $JSI.$eval
 * @access public
 * @param {string} s 要执行的脚本
 * @return {mixed} 执行结果
 */
$JSI.$eval = function(s)
{
	return eval(s);
};

/** 
 * void function
 * @typeof function
 * @id $JSI.$void
 * @access public
 */
$JSI.$void = function(){};

(function()
{
	if (this.ActiveXObject && (!this.XMLHttpRequest || location.protocol == "file:"))
	{
		//XMLHttpRequest ActiveObject ProgIds
		var progId = ["Microsoft.XMLHTTP"
						, "MSXML2.XMLHTTP"
						, "MSXML2.XMLHTTP.3.0"
						, "Msxml2.XMLHTTP.4.0"
						, "Msxml2.XMLHTTP.5.0"
						, "MSXML2.XMLHTTP.6.0"];
		/**
		 * 统一的 XMLHttpRequest 构造器（对于ie，做一个有返回值的构造器（这时new操作返回该返回值），返回他支持的AxtiveX控件） 
		 * 关于 XMLHttpRequest对象的详细信息请参考 
		 * <ul> 
		 *   <li><a href="http://www.w3.org/TR/XMLHttpRequest/">W3C XMLHttpRequest</a></li> 
		 *   <li><a href="http://www.ikown.com/manual/xmlhttp/index.htm">中文参考</a></li> 
		 * </ul>
		 * <br><br><i>JSDOC ： 对于FF等有内置 XMLHttpRequest 对象的浏览器，jsdoc解析时有可能会报错。请直接参看上述链接文档，不必理会jsdoc的解析结果。</i> 
		 * @constructor
		 */
		this.XMLHttpRequest = function()
		{
			for (var i = progId.length - 1, o; i >= 0; i--)
			{
				try
				{
					o = new ActiveXObject(progId[i]);
					progId = progId[i];
					XMLHttpRequest = function ()
					{
						return new ActiveXObject(progId);
					}
					return o;
				}
				catch (e)
				{
				}
			}
		}
	}
})();
//#appendding core here#
//}